/**
 * 任务对象
 */
type Ttask<T> = {
    /** 任务时间 */
    time: number;
    /** 任务内容 */
    job: T;
};

/**
 * 任务执行方法
 */
type Ttask_fn<T> = (
    /** 传入的任务内容 */
    job: T,
    /** 任务相关的时间 */
    times: {
        /** 总计划开始时间 */
        start: number,
        /** 任务回调时间 */
        callback: number,
        /** 任务回调时间与计划开始时间差值 */
        offset: number,
        /** 传入的任务时间 */
        task: number,
        /** 任务真实执行时间与传入的任务时间差值 */
        delay: number,
    }
) => void;

/**
 * 停止任务执行方法
 * @returns true: 停止任务; false: 继续任务;
 */
type T_task_stop_fn<T> = (
    /** 传入的任务内容 */
    job: undefined | T,
) => boolean;

/**
 * 完成计划后执行的方法
 * @returns true: 停止任务; false: 继续任务;
 */
type Ttask_finish_fn = (
    /** 任务相关的时间 */
    times: {
        /** 剩余未执行的任务数 */
        remaining: number;
        /** 总计划开始时间 */
        start: number;
        /** 总计划结束时间 */
        end: number;
        /** 最后一个任务回调时间 */
        callback: number;
        /** 最后一个任务回调时间与计划开始时间差值 */
        offset: number;
    }
) => void;

interface _CU_ {
    /**
     * 执行计划任务
     * @param tasks 任务列表
     * @param exeTaskFn 任务执行方法
     * @param stopFn 停止任务执行方法
     * @param finishFn 完成任务后执行的方法
     */
    exeTasks<T>(
        tasks: Array<Ttask<T>>,
        exeTaskFn: Ttask_fn<T>,
        stopFn?: T_task_stop_fn<T>,
        finishFn?: Ttask_finish_fn,
    ): void;
}
{
    Object.defineProperty(_CU_.prototype, 'exeTasks', {
        writable: false,
        configurable: false,
        enumerable: false,
        value: <T>(
            tasks: Array<Ttask<T>>,
            exeTaskFn: Ttask_fn<T>,
            stopFn?: T_task_stop_fn<T>,
            finishFn?: Ttask_finish_fn,
        ) => {
            if (tasks.length === 0) { return; }

            stopFn = stopFn || U.F.fn.false as T_task_stop_fn<T>;
            finishFn = finishFn || U.F.fn.void as Ttask_finish_fn;

            // 按照任务时间排序
            tasks.sort((a, b) => a.time - b.time);

            const callback = (currentTime: number) => {
                const gapTime = currentTime - startTime;

                if (stopFn(void 0)) {
                    finishFn({
                        remaining: tasks.length,
                        start: startTime,
                        end: performance.now(),
                        callback: currentTime,
                        offset: gapTime,
                    });
                    return;
                }// 停止后续任务

                // 超过当前时间差的任务即需要再当前帧执行的任务
                let taskCnt = 0;
                for (let i = 0; i < tasks.length; i++) {
                    const task = tasks[i];
                    if (task.time < gapTime) {
                        taskCnt++;
                    }
                }

                // 有需要当前帧执行的任务，则执行该任务。
                if (taskCnt != 0) {
                    for (let i = 0; i < taskCnt; i++) {
                        const task = tasks.shift();
                        if (typeof task === 'undefined') { continue; }

                        exeTaskFn(task.job, {
                            start: startTime,
                            callback: currentTime,
                            offset: gapTime,
                            task: task.time,
                            delay: gapTime - task.time,
                        });

                        if (stopFn(task.job)) {
                            finishFn({
                                remaining: tasks.length,
                                start: startTime,
                                end: performance.now(),
                                callback: currentTime,
                                offset: gapTime,
                            });
                            return;
                        }// 停止后续任务
                    }
                }

                if (tasks.length == 0) {// 没有任务，结束。
                    finishFn({
                        remaining: 0,
                        start: startTime,
                        end: performance.now(),
                        callback: currentTime,
                        offset: gapTime,
                    });
                    return;
                }

                // 还有任务，继续递归。
                requestAnimationFrame(callback);
            };

            let startTime = performance.now();
            requestAnimationFrame(callback);
        }
    });
}
